package com.fz.service.merchant.impl;

import cn.hutool.core.lang.UUID;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fz.common.Global;
import com.fz.common.GlobalAll;
import com.fz.common.ResponseEntity;
import com.fz.common.ResultCode;
import com.fz.entity.merchant.MerUser;
import com.fz.mapper.merchant.MerUserMapper;
import com.fz.security.config.SecurityProperties;
import com.fz.security.provider.TokenProvider;
import com.fz.security.utils.SecurityUtils;
import com.fz.security.vo.JwtUser;
import com.fz.security.vo.LoginVo;
import com.fz.service.merchant.MerLoginService;
import com.fz.service.merchant.MerUserService;
import com.fz.utils.JacksonUtils;
import com.fz.utils.RedisUtil;
import com.fz.utils.StringUtils;
import com.fz.vo.merchant.merCode.MerCodeVo;
import com.wf.captcha.ArithmeticCaptcha;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

@Service
@SuppressWarnings("unchecked")
public class MerLoginServiceImpl implements MerLoginService {
    private final PasswordEncoder passwordEncoder;
    @Resource
    private RedisUtil redisUtil;
    @Autowired
    public AuthenticationManagerBuilder authenticationManagerBuilder;
    @Autowired
    public TokenProvider tokenProvider;
    @Autowired
    public SecurityProperties securityProperties;
    @Autowired
    public MerUserService merUserService;
    @Resource
    public MerUserMapper merUserMapper;

    private static final Logger logger = LoggerFactory.getLogger(MerLoginServiceImpl.class);

    //密码效验
    public MerLoginServiceImpl(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    /**
     * @方法描述: system平台登录
     * @参数:
     * @返回值:
     * @创建人: I am God
     * @创建时间:: 2021/2/22
     */
    public ResponseEntity login(String loginid, String password,
                                String randCode, String randCodeKey, HttpServletRequest request) throws Exception {

        //查询登录名是否存在
        LambdaQueryWrapper<MerUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(MerUser::getLoginId, loginid);
        MerUser user = merUserMapper.selectOne(lambdaQueryWrapper);
        if (null == user) {
            return ResponseEntity.build(ResultCode.NO_EXIT_USER);
        }
        //用户状态 -- 代表用户禁用
        if (user.getStatus() == 1) {
            //如果禁用备注为空
            if (StringUtils.isEmpty(user.getDisableRemark())) {
                return ResponseEntity.build(ResultCode.USER_DISABLE);
            } else {
                //将禁用原因返回给前端
                return ResponseEntity.build(ResultCode.USER_DISABLE.getResultCode(), user.getDisableRemark());
            }
        }

        //验证验证码是否正确
        String code = String.valueOf(redisUtil.get(randCodeKey));
        //清除验证码
        redisUtil.del(randCodeKey);
        if (StringUtils.isEmpty(code)) {
            return ResponseEntity.build(ResultCode.CODE_NOEXIT_EXPIRE);
        }

        if (StringUtils.isEmpty(randCode) || !randCode.equals(code)) {
            return ResponseEntity.build(ResultCode.CODE_ERROR);
        }
        // 密码解密 解密成security密码
        RSA rsa = new RSA(GlobalAll.RSA_PRIVATE_KEY, null);
        String jmpassword = new String(rsa.decrypt(password, KeyType.PrivateKey));
        //密码错误
        boolean flag = passwordEncoder.matches(jmpassword, user.getPassword());
        String errorKey = Global.PWD_ERROR_KEY + user.getId() + user.getLoginId();
        if (flag == false) {
            if (redisUtil.hasKey(errorKey)) {
                //如果存在过输入错误密码
                int errNum = (Integer) redisUtil.get(errorKey);
                redisUtil.set(errorKey, errNum + 1);
                if ((Integer) redisUtil.get(errorKey) >= 5) {
                    //更改用户状态
                    user.setStatus(1);
                    user.setDisableRemark(ResultCode.PWD_CON_ERROR.getMessage());
                    merUserMapper.updateById(user);
                    logger.info(user.getLoginId()+":"+ResultCode.PWD_CON_ERROR.getMessage());
                    return ResponseEntity.build(ResultCode.PWD_CON_ERROR);
                }
            } else {
                //如果第一次输错密码
                redisUtil.set(errorKey, 1);
            }
            return ResponseEntity.build(ResultCode.PWD_ERROR);
        } else {
            //当登录密码成功时
            if (redisUtil.hasKey(errorKey)) {     //如果存在key
                redisUtil.del(errorKey);
            }
        }
        //调用Authentication
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(loginid, jmpassword);
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        final JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
        // 生成token令牌
        String token = tokenProvider.createToken(jwtUser,authentication);
        LoginVo loginVo = new LoginVo();
        loginVo.setJwtUser(jwtUser);
        loginVo.setToken(securityProperties.getTokenStartWith() + token);
        //如果存在key
        String key = Global.TOKEN_KEY_HEAD +user.getId() + Global.TOKEN_SPLIT + user.getLoginId();
        //获取同账号的上一个token,用于验证同账号只可在线一个设备
        if(redisUtil.hasKey(key)){
            String oldToken = redisUtil.get(key).toString();
            redisUtil.set(Global.OLD_TOKEN + oldToken,oldToken ,securityProperties.getTokenValidityInSeconds() / 1000);
        }
        //保存用户基本信息与token的有效期一致 同为4个小时
        redisUtil.set(token, JacksonUtils.obj2String(jwtUser), securityProperties.getTokenValidityInSeconds() / 1000);
        //将用户的token存下来,与用户基本信息有效期一致 同为4个小时
        redisUtil.set(key,token,securityProperties.getTokenValidityInSeconds() / 1000);
        return ResponseEntity.buildSuccess(loginVo);
    }

    /**
     * @方法描述: 生成图片验证码
     * @参数:
     * @返回值:
     * @创建人: I am God
     * @创建时间:: 2021/2/22
     */
    public ResponseEntity randCode() throws Exception {
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(111, 36);
        captcha.setLen(2);
        String result = captcha.text();
        String key = Global.RAND_CODE_KEY + UUID.randomUUID();
        redisUtil.set(key, result, 120);
        MerCodeVo codeVo = new MerCodeVo();
        codeVo.setImg(captcha.toBase64());
        codeVo.setRandCodeKey(key);
        return ResponseEntity.buildSuccess(codeVo);
    }

    /**
     * @方法描述:
     * @参数: 用户登出
     * @返回值:
     * @创建人: I am God
     * @创建时间:: 2021/2/23
     */
    @Override
    public ResponseEntity logout(String token, HttpServletRequest request) throws Exception {
        MerUser user = SecurityUtils.getCurrentUser();
        //清除用户的基本信息
        redisUtil.del(token);
        //清除用户的token
        redisUtil.del(Global.TOKEN_KEY_HEAD +user.getId() + Global.TOKEN_SPLIT + user.getLoginId());
        return ResponseEntity.buildSuccess();
    }

}
